﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace SolutionSettings {
    internal interface IFileSystem {
        bool FileExists(string path);
        bool DirectoryExists(string path);

        void MoveFile(string oldPath, string newPath);
        void MoveDirectory(string oldPath, string newPath);

        TextReader Open(string path);
        TextWriter OpenWrite(string path);
    }

    internal class DefaultFileSystem : IFileSystem {
        public bool FileExists(string path) {
            return File.Exists(path);
        }

        public bool DirectoryExists(string path) {
            return Directory.Exists(path);
        }

        public void MoveFile(string oldPath, string newPath) {
            File.Move(oldPath, newPath);
        }

        public void MoveDirectory(string oldPath, string newPath) {
            Directory.Move(oldPath, newPath);
        }

        public TextReader Open(string path) {
            return new StreamReader(path, true);
        }

        public TextWriter OpenWrite(string path) {
            return new StreamWriter(path, false, Encoding.Unicode);
        }
    }

    internal class MockFile {
        public MockFile(string filePath, string contents) {
            Path = filePath;
            Contents = new MemoryStream();
            if (!string.IsNullOrEmpty(contents)) {
                new StreamWriter(Contents).Write(contents);
            }
        }

        public MockFile(string directoryPath) {
            Path = directoryPath;
            Contents = null;
        }
        
        private string _Path;
        public string Path {
            get { return _Path; }
            set { _Path = value.TrimEnd('/', '\\'); }
        }
        public MemoryStream Contents { get; set; }
        public string GetContents() {
            return new StreamReader(Contents).ReadToEnd();
        }

        public bool IsDirectory {
            get { return Contents == null; }
            set { Contents = value ? null : new MemoryStream(); }
        }
    }

    internal class MockFileSystem : IFileSystem {
        public List<MockFile> Files;

        public MockFileSystem() {
            Files = new List<MockFile>();
        }
        
        public bool FileExists(string path) {
            return Files.SingleOrDefault(f => string.Equals(path, f.Path, StringComparison.OrdinalIgnoreCase) &&
                !f.IsDirectory) != null;
        }

        public bool DirectoryExists(string path) {
            return Files.SingleOrDefault(f => string.Equals(path, f.Path, StringComparison.OrdinalIgnoreCase) &&
                f.IsDirectory) != null;
        }

        public void MoveFile(string oldPath, string newPath) {
            var file = Files.SingleOrDefault(f => string.Equals(oldPath, f.Path, StringComparison.OrdinalIgnoreCase) &&
                !f.IsDirectory);

            if (file == null) {
                throw new FileNotFoundException();
            }
            newPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(oldPath), newPath));
            if (FileExists(newPath)) {
                throw new IOException();
            }
            if (!DirectoryExists(Path.GetDirectoryName(newPath))) {
                throw new DirectoryNotFoundException();
            }

            file.Path = newPath;
        }

        public void MoveDirectory(string oldPath, string newPath) {
            var file = Files.SingleOrDefault(f => string.Equals(oldPath, f.Path, StringComparison.OrdinalIgnoreCase) &&
                f.IsDirectory);

            if (file == null) {
                throw new FileNotFoundException();
            }
            newPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(oldPath), newPath));
            if (DirectoryExists(newPath)) {
                throw new IOException();
            }
            if (!DirectoryExists(Path.GetDirectoryName(newPath))) {
                throw new DirectoryNotFoundException();
            }

            file.Path = newPath;
        }


        public TextReader Open(string path) {
            var file = Files.SingleOrDefault(f => string.Equals(path, f.Path, StringComparison.OrdinalIgnoreCase) &&
                !f.IsDirectory);

            if (file == null) {
                throw new FileNotFoundException();
            }

            return new StreamReader(file.Contents);
        }

        public TextWriter OpenWrite(string path) {
            var file = Files.SingleOrDefault(f => string.Equals(path, f.Path, StringComparison.OrdinalIgnoreCase) &&
                !f.IsDirectory);

            if (file == null) {
                file = new MockFile(path, null);
                Files.Add(file);
            }

            return new StreamWriter(file.Contents);
        }
    }


}
